// include the basic windows header file
#include <windows.h>
#include <windowsx.h>
#include "GameController.h"
#include "ResourceManager.h"
#include "MapController.h"
#include "DXGraphics.h"

#include "DirectInput.h"
#include "AudioPlay.h"

enum difficulty{ EASY, NORMAL, HARD } ;

// the WindowProc function prototype
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

LPDIRECT3DDEVICE9 gD3DDevice = 0 ;

class GameController
{
private:
	Map * map ;
	AudioPlay * audio ;
	bool m_running, m_buttonClicked, m_flagButtonClicked, m_resetButtonDown, m_lost ;

	ResourceManager * res ;

	int m_gameState ;
public:
	GameController( ) : m_running(true), m_buttonClicked(false), m_flagButtonClicked(false), m_lost(false), m_resetButtonDown(false), m_gameState(MAIN)
	{}

	~GameController() 
	{ 
		delete res ;
	}

	void GameController::update( ) 
	{
		gDGraphics->Update() ;
		POINT mouseCursor = gDGraphics->getMousePos() ;
		//get DirectInput states
		gDInput->poll( ) ;

		if( m_gameState == PLAY )
		{
			//if player hasn't lost the game
			if( !m_lost )
			{
				//left-click
				if( gDInput->mouseButtonDown(0) )
				{
					if( !m_buttonClicked )
					{
						m_buttonClicked = true ;
				
						//checks if cursor clicked on a tile and if it is hidden and not flagged
						for( int col = 0; col < map->getWidth(); ++col )
							for( int row = 0; row < map->getHeight(); ++row )
							{
								if( mouseCursor.x >= (col * SPRITE_SIZE + map->getOffset()) &&
									mouseCursor.x < ((col+1) * SPRITE_SIZE + map->getOffset()) &&
									mouseCursor.y >= (row * SPRITE_SIZE) &&
									mouseCursor.y < ((row+1) * SPRITE_SIZE) &&
									map->isHidden(col, row) &&
									!map->isFlagged(col, row) )
								{
									//get sound to play
									audio = res->getSound(res->getIndex("Sounds/click.mp3")) ;
									audio->playSound() ;

									//reveal tile
									map->reveal(col, row) ;
								}
							}
						}
					}
				else if( m_buttonClicked )
					m_buttonClicked = false ;

				//right-click
				if( gDInput->mouseButtonDown(1) )
				{
					if( !m_flagButtonClicked )
					{
						m_flagButtonClicked = true ;
				
						//checks if clicked on a tile and if it is still hidden
						for( int col = 0; col < map->getWidth(); ++col )
							for( int row = 0; row < map->getHeight(); ++row )
							{
								if( mouseCursor.x >= (col * SPRITE_SIZE + map->getOffset()) &&
									mouseCursor.x < ((col+1) * SPRITE_SIZE + map->getOffset()) &&
									mouseCursor.y >= (row * SPRITE_SIZE) &&
									mouseCursor.y < ((row+1) * SPRITE_SIZE) &&
									map->isHidden(col, row) )
								{
									//grab audio for click
									audio = res->getSound(res->getIndex("Sounds/click.mp3")) ;
									audio->playSound() ;

									//flag/unflag tile
									if( !map->isFlagged(col, row) )
									{
										if( map->canFlag() )
											map->flag(col, row) ;
									}
									else
										map->unflag(col, row) ;
								}
							}
					}
				}
				else if( m_flagButtonClicked )
					m_flagButtonClicked = false ;

				//checks if player lost the game
				if(map->lossCondition())
				{
					//explosion
					audio = res->getSound(res->getIndex("Sounds/mine.mp3")) ;
					audio->playSound() ;

					//reveal all mines
					map->revealMines() ;
					m_lost = true ;
				}

				//checks if player won the game
				if( map->winCondition( ) )
				{
					//clear map for next game
					map->clearMap() ;

					//change game state
					m_gameState = WIN ;
				}
			}

			//resets the current game
			if( gDInput->keyDown(DIK_R) )
			{
				if( !m_resetButtonDown )
				{
					m_resetButtonDown = true ;
					audio = res->getSound(res->getIndex("Sounds/jump.wav")) ;
					audio->playSound() ;
					m_lost = false ;
					map->clearMap() ;
					map->generateMap() ;
				}
			}
			else if( m_resetButtonDown )
				m_resetButtonDown = false ;

			//quits the current game
			if( gDInput->keyDown(DIK_Q) )
			{
				m_lost = false ;
				map->clearMap() ;
				gDGraphics->createBox( SCREEN_WIDTH, 0, 0 ) ;
				m_gameState = MAIN ;
			}

		}
		else
		{
			//EASY GAME
			if( gDInput->keyDown(DIK_1) )
			{
				map = res->getMap(res->getIndex("Maps/map_easy.txt")) ;
				map->generateMap() ;
				gDGraphics->createBox( map->getWidth()*SPRITE_SIZE, map->getHeight()*SPRITE_SIZE, map->getOffset() ) ;
				m_gameState = PLAY ;
			}

			//NORMAL GAME
			if( gDInput->keyDown(DIK_2) )
			{
				map = res->getMap(res->getIndex("Maps/map_normal.txt")) ;
				map->generateMap() ;
				gDGraphics->createBox( map->getWidth()*SPRITE_SIZE, map->getHeight()*SPRITE_SIZE, map->getOffset() ) ;
				m_gameState = PLAY ;
			}

			//HARD GAME
			if( gDInput->keyDown(DIK_3) )
			{
				map = res->getMap(res->getIndex("Maps/map_hard.txt")) ;
				map->generateMap() ;
				gDGraphics->createBox( map->getWidth()*SPRITE_SIZE, map->getHeight()*SPRITE_SIZE, map->getOffset() ) ;
				m_gameState = PLAY ;
			}

			//QUIT GAME
			if( gDInput->keyDown(DIK_ESCAPE) )
				m_running = false ;
		}

		//render game
		draw( ) ;
	}
	//find and initialize files here
	void init( ) 
	{
		res = ResourceManager::instance() ;

		//-----------------------Map txt files-----------------------
		res->loadResource("Maps/map_easy.txt", MAP) ;
		res->loadResource("Maps/map_normal.txt", MAP) ;
		res->loadResource("Maps/map_hard.txt", MAP) ;
		//----------------------Sprite files-------------------------
		res->loadResource("Sprites/sp_mine.png", SPRITE) ;
		res->loadResource("Sprites/sp_tile_one.png", SPRITE) ;
		res->loadResource("Sprites/sp_tile_two.png", SPRITE) ;
		res->loadResource("Sprites/sp_tile_three.png", SPRITE) ;
		res->loadResource("Sprites/sp_tile_four.png", SPRITE) ;
		res->loadResource("Sprites/sp_tile_five.png", SPRITE) ;
		res->loadResource("Sprites/sp_tile_six.png", SPRITE) ;
		res->loadResource("Sprites/sp_tile_seven.png", SPRITE) ;
		res->loadResource("Sprites/sp_tile_eight.png", SPRITE) ;
		res->loadResource("Sprites/sp_tile_hidden.png", SPRITE) ;
		res->loadResource("Sprites/sp_tile_none.png", SPRITE) ;
		res->loadResource("Sprites/sp_tile_flagged.png", SPRITE) ;
		res->loadResource("Sprites/sp_cursor.png", SPRITE) ;
		//----------------------Sound Files-------------------------
		res->loadResource("Sounds/click.mp3", FX) ;
		res->loadResource("Sounds/mine.mp3", FX) ;
		res->loadResource("Sounds/jump.wav", FX) ;

		//create the textbox size for initial menu
		gDGraphics->createBox( SCREEN_WIDTH, 0, 0 ) ;
	}

	void draw( )
	{
		gDGraphics->render_frame( map, m_gameState ) ;
	}

	bool isRunning( )
	{
		return m_running ;
	}
} ;

// the entry point for any Windows program
int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow)
{
	// Enable run-time memory check for debug builds.
	#if defined(DEBUG) | defined(_DEBUG)
		_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
	#endif

	GameController * game = new GameController() ;

    HWND hWnd;
    WNDCLASSEX wc;
	int windowX = 0 ;
	int windowY = 0 ;

    ZeroMemory(&wc, sizeof(WNDCLASSEX));

    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
    wc.lpszClassName = L"WindowClass";

    RegisterClassEx(&wc);

    hWnd = CreateWindowEx(NULL,
                          L"WindowClass",
						  L"Minesweeper by David Fairweather",
                          WS_OVERLAPPEDWINDOW,
                          windowX, windowY,
                          SCREEN_WIDTH, SCREEN_HEIGHT,
                          NULL,
                          NULL,
                          hInstance,
                          NULL);

	if( !hWnd )
	{
		MessageBoxA(0, "CreateWindow FAILED", 0, 0);
		PostQuitMessage(0);
	}

    ShowWindow(hWnd, nCmdShow);
	UpdateWindow(hWnd);

	gDInput = new DirectInput(DISCL_NONEXCLUSIVE |
		DISCL_FOREGROUND,
		DISCL_NONEXCLUSIVE |
		DISCL_FOREGROUND, hInstance, hWnd ) ;

	gDGraphics = new DXGraphics() ;

    // set up and initialize Direct3D
    gDGraphics->initD3D(hWnd) ;
	game->init( ) ;

    // enter the main loop:

    MSG msg ;

	//game loop
	while(game->isRunning())
    {
        DWORD starting_point = GetTickCount();

        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            if (msg.message == WM_QUIT)
                break;

            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

		//game update
		game->update( ) ;

        while ((GetTickCount() - starting_point) < 25);
    }

	//delete game controller
	delete game ;
	delete gDGraphics ;
	delete gDInput ;

    return msg.wParam;
}


// this is the main message handler for the program
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch(message)
    {
        case WM_DESTROY:
            {
                PostQuitMessage(0);
                return 0;
            } break;
    }

    return DefWindowProc (hWnd, message, wParam, lParam);
}